<template>
  <!-- components: Uploader -->
  <div class = "file-upload">
    <div
      class = "file-upload-container"
      v-on:click.prevent="triggerUpload"
      v-bind="$attrs"
    >
      <slot name="loading" v-if="fileStatus === 'loading'">
        <button class = "btn btn-primary" disabled>正在上传...</button>
      </slot>
      <slot name="uploaded" v-else-if="fileStatus === 'success'" v-bind:uploadedData="uploadedData">
        <button class = "btn btn-primary">上传成功</button>
      </slot>
      <slot name="default" v-else>
        <button class = "btn btn-primary">点击上传</button>
      </slot>
    </div>
    <input
      type = "file"
      class = "file-input d-none"
      ref="fileInput"
      v-on:change="handleFileChange"
    />
  </div>
</template>

<script lang = "ts">
import { defineComponent, PropType, ref, watch } from 'vue'
// 导入 axios
import axios from 'axios'

type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type CheckFunction = (file: File) => boolean
// 对setup函数进行封装，返回options的对象
export default defineComponent({
  // 页面名称
  name: 'Uploader',
  // 传递数据
  props: {
    action: {
      type: String,
      required: true
    },
    beforeUpload: {
      type: Function as PropType<CheckFunction>
    },
    uploaded: {
      type: Object
    }
  },

  inheritAttrs: false,
  // 发送事件
  emits: ['file-uploaded', 'file-uploaded-error'],

  // 组合式api入口
  setup (props, context) {
    // 响应式对象 - 文件
    const fileInput = ref<null | HTMLInputElement>(null)
    // 响应式对象 - 状态<泛型>
    const fileStatus = ref<UploadStatus>(props.uploaded ? 'success' : 'ready')
    // 响应式对象 - 数据
    const uploadedData = ref(props.uploaded)
    // 监听文章数据
    watch(() => props.uploaded, (newValue) => {
      if (newValue) {
        fileStatus.value = 'success'
        uploadedData.value = newValue
      }
    })
    // 更新
    const triggerUpload = () => {
      if (fileInput.value) {
        fileInput.value.click()
      }
    }
    // 上传文件
    const handleFileChange = (e: Event) => {
      const currentTarget = e.target as HTMLInputElement
      if (currentTarget.files) {
        const files = Array.from(currentTarget.files)
        if (props.beforeUpload) {
          const result = props.beforeUpload(files[0])
          if (!result) {
            return
          }
        }
        fileStatus.value = 'loading'
        const formData = new FormData()
        formData.append('file', files[0])
        axios.post(props.action, formData, {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        }).then(resp => {
          // 上传成功
          fileStatus.value = 'success'
          uploadedData.value = resp.data
          // 触发上传事件
          context.emit('file-uploaded', resp.data)
        }).catch((error) => {
          // 上传失败
          fileStatus.value = 'error'
          // 触发上传失败事件
          context.emit('file-uploaded-error', { error })
        }).finally(() => {
          // 结束上传操作
          if (fileInput.value) {
            fileInput.value.value = ''
          }
        })
      }
    }
    return {
      fileInput,
      fileStatus,
      triggerUpload,
      handleFileChange,
      uploadedData
    }
  }
})
</script>

<style lang="css" scoped>
</style>
